﻿using System;
using System.Runtime.CompilerServices;
using QuantBox.QuantDo.Api;

namespace QuantBox.XApi
{
    internal static class QdpConvert
    {
        private const string ClientId = "QDP";

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static QdpResumeType GetQdpResumeType(ResumeType type)
        {
            switch (type)
            {
                case ResumeType.Restart:
                    return QdpResumeType.Restart;
                case ResumeType.Resume:
                    return QdpResumeType.Resume;
                default:
                    return QdpResumeType.Quick;
            }
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static string GetReasonMsg(int reason)
        {
            switch (reason)
            {
                case 0x1001:
                    return "0x1001 4097 网络读失败";
                case 0x1002:
                    return "0x1002 4098 网络写失败";
                case 0x2001:
                    return "0x2001 8193 接收心跳超时";
                case 0x2002:
                    return "0x2002 8194 发送心跳失败";
                case 0x2003:
                    return "0x2003 8195 收到错误报文";
                case 0x2004:
                    return "0x2004 8196 服务器主动断开";
                default:
                    return $"0x{reason:X} {reason} 未知错误";
            }
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static bool IsInvalid(double value)
        {
            return double.IsNaN(value)
                   || Math.Abs(value) < double.Epsilon
                   || Math.Abs(double.MaxValue - value) < double.Epsilon
                   || Math.Abs(value - double.MinValue) < double.Epsilon;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static bool CheckRspInfo(QdpRspInfo? info)
        {
            return info == null || info.ErrorId == 0;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static int GetDate(string date)
        {
            return string.IsNullOrEmpty(date) ? 99991231 : int.Parse(date);
        }

        public static int GetTime(string time)
        {
            if (time.Length == 6)
            {
                if (time[1] != ':' && time[2] != ':')
                {
                    return int.Parse(time);
                }
            }
            if (time.Length == 8)
            {
                return int.Parse(time.Substring(0, 2)) * 10000
                       + int.Parse(time.Substring(3, 2)) * 100
                       + int.Parse(time.Substring(6, 2));
            }

            var item = time.Split(':');
            if (item.Length >= 3)
            {
                return int.Parse(item[0]) * 10000
                       + int.Parse(item[1]) * 100
                       + int.Parse(item[2]);
            }

            return 0;
        }

        public static void SetExchangeTime(QdDepthMarketData data, DepthMarketDataField market)
        {
            market.TradingDay = GetDate(data.TradingDay);
            market.UpdateTime = GetTime(data.UpdateTime);
            market.UpdateMillisec = data.UpdateMillisec;
            if (market.UpdateTime < 60000)
            {
                market.ActionDay = market.TradingDay;
            }
            else
            {
                var now = DateTime.Today;
                market.ActionDay = now.Year * 10000 + now.Month * 100 + now.Day;
            }
        }

        public static InstrumentStatusField GetInstrumentStatus(QdpInstrumentStatus status)
        {
            var field = new InstrumentStatusField();
            field.Status = GetInstrumentStatusType(status.InstrumentStatus);
            field.EnterReason = EnterReasonType.Automatic;
            field.EnterTime = GetTime(DateTime.Now.ToString("HH:mm:ss"));
            field.InstrumentID = status.InstrumentId;
            field.ExchangeID = status.ExchangeId;
            return field;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static InstrumentStatusType GetInstrumentStatusType(byte type)
        {
            switch (type)
            {
                case QdpInstrumentStatusType.AuctionBalance:
                    return InstrumentStatusType.AuctionBalance;
                case QdpInstrumentStatusType.AuctionMatch:
                    return InstrumentStatusType.AuctionMatch;
                case QdpInstrumentStatusType.AuctionOrdering:
                    return InstrumentStatusType.AuctionOrdering;
                case QdpInstrumentStatusType.BeforeTrading:
                    return InstrumentStatusType.BeforeTrading;
                case QdpInstrumentStatusType.Closed:
                    return InstrumentStatusType.Closed;
                case QdpInstrumentStatusType.Continous:
                    return InstrumentStatusType.Continous;
                default:
                    return InstrumentStatusType.NoTrading;
            }
        }

        public static InstrumentField GetInstrumentField(QdpRspInstrument info)
        {
            var inst = new InstrumentField();
            inst.InstrumentID = info.InstrumentId;
            inst.ExchangeID = info.ExchangeId;
            inst.Symbol = info.InstrumentId;
            inst.ProductID = info.ProductId;
            inst.SetName(info.InstrumentName);
            inst.Type = GetInstrumentType(info.ProductClass);
            inst.VolumeMultiple = info.VolumeMultiple;
            inst.PriceTick = info.PriceTick;
            inst.ExpireDate = GetDate(info.ExpireDate);
            inst.OptionsType = GetPutCall(info.OptionsType);
            inst.StrikePrice = (info.StrikePrice < double.Epsilon || Math.Abs(info.StrikePrice - double.MaxValue) < double.Epsilon) ? 0 : info.StrikePrice;
            inst.UnderlyingInstrID = info.UnderlyingInstrId;
            return inst;
        }

        internal static AccountField GetAccountField(QdpRspInvestorAccount info)
        {
            var account = new AccountField();
            account.CurrencyID = "CNY";
            account.AccountID = info.AccountId;
            account.ClientID = info.InvestorId;
            account.PreBalance = info.PreBalance;
            account.CurrMargin = info.Margin;
            account.Commission = 0;
            account.CloseProfit = info.CloseProfit;
            account.PositionProfit = info.PositionProfit;
            account.Balance = info.Balance;
            account.Available = info.Available;
            account.Deposit = info.Deposit;
            account.Withdraw = info.Withdraw;
            account.WithdrawQuota = 0;
            return account;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private static PutCall GetPutCall(byte type)
        {
            if (type == QdpOptionsTypeType.CallOptions)
            {
                return PutCall.Call;
            }
            return PutCall.Put;
        }

        private static InstrumentType GetInstrumentType(byte productClass)
        {
            switch (productClass)
            {
                case QdpProductClassType.Futures:
                case QdpProductClassType.Efp:
                    return InstrumentType.Future;
                case QdpProductClassType.Combination:
                    return InstrumentType.MultiLeg;
                case QdpProductClassType.Options:
                    return InstrumentType.FutureOption;
                case QdpProductClassType.StockOptions:
                    return InstrumentType.Option;
                default:
                    return InstrumentType.Stock;
            }
        }

        public static PositionField GetPositionField(QdpRspInvestorPosition info)
        {
            var position = new PositionField();
            position.Date = int.Parse(DateTime.Today.ToString("yyyyMMdd"));
            position.InstrumentID = info.InstrumentId;
            position.Symbol = info.InstrumentId;
            position.AccountID = info.InvestorId;
            position.Position = info.Position;
            position.OpenCost = info.PositionCost;
            position.PositionCost = info.PositionCost;
            position.TodayPosition = info.TodayPosition;
            position.HistoryPosition = info.YdPosition;
            position.Side = GetPositionSide(info.Direction);
            position.HedgeFlag = GetHedgeFlag(info.HedgeFlag);
            return position;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static HedgeFlagType GetHedgeFlag(byte flag)
        {
            switch (flag)
            {
                case QdpHedgeFlagType.Arbitrage:
                    return HedgeFlagType.Arbitrage;
                case QdpHedgeFlagType.Hedge:
                    return HedgeFlagType.Hedge;
                case QdpHedgeFlagType.Speculation:
                    return HedgeFlagType.Speculation;
            }
            return HedgeFlagType.Undefined;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static PositionSide GetPositionSide(byte dir)
        {

            if (dir == QdpDirectionType.Buy)
            {
                return PositionSide.Long;
            }
            return PositionSide.Short;
        }

        public static ExecType GetExecType(QdpOrder data)
        {
            switch (data.OrderStatus)
            {
                case QdpOrderStatusType.Canceled:
                    return ExecType.Cancelled;
                case QdpOrderStatusType.AllTraded:
                case QdpOrderStatusType.PartTradedQueueing:
                    return ExecType.Trade;
                default:
                    return ExecType.New;
            }
        }

        public static OrderField? GetOrder(QdpOrder? data)
        {
            if (data == null)
            {
                return null;
            }
            var order = new OrderField();
            order.ID = data.UserCustom;
            //order.LocalID = data.UserOrderLocalId;
            order.OrderID = data.OrderSysId;
            order.InstrumentID = data.InstrumentId;
            order.Qty = data.Volume;
            order.Side = GetOrderSide(data.Direction);
            order.OpenClose = GetOpenClose(data.OffsetFlag);
            order.HedgeFlag = GetHedgeFlag(data.HedgeFlag);
            order.Price = data.LimitPrice;
            order.StopPx = data.StopPrice;
            order.TimeInForce = TimeInForce.IOC;
            order.Type = order.Price > 0 ? OrderType.Limit : OrderType.Market;
            order.Status = GetOrderStatus(data);
            order.ExecType = GetExecType(data);
            order.Date = GetDate(data.TradingDay);
            order.Time = GetTime(data.InsertTime);
            order.ReserveChar64 = ClientId;
            return order;
        }

        internal static OrderStatus GetOrderStatus(QdpOrder data)
        {
            switch (data.OrderStatus)
            {
                case QdpOrderStatusType.Canceled:
                    return OrderStatus.Cancelled;
                case QdpOrderStatusType.AllTraded:
                    return OrderStatus.Filled;
                case QdpOrderStatusType.PartTradedQueueing:
                    return OrderStatus.PartiallyFilled;
                default:
                    return OrderStatus.New;
            }
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private static OpenCloseType GetOpenClose(byte flag)
        {
            switch (flag)
            {
                case QdpOffsetFlagType.Close:
                case QdpOffsetFlagType.CloseYesterday:
                    return OpenCloseType.Close;
                case QdpOffsetFlagType.CloseToday:
                    return OpenCloseType.CloseToday;
                default:
                    return OpenCloseType.Open;
            }
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private static OrderSide GetOrderSide(byte direction)
        {
            if (direction == QdpDirectionType.Buy)
            {
                return OrderSide.Buy;
            }
            return OrderSide.Sell;
        }

        public static TradeField GetTrade(QdpTrade data)
        {
            var trade = new TradeField();
            //trade.ID = data.UserOrderLocalId;
            trade.ClientID = data.OrderSysId;
            trade.InstrumentID = data.InstrumentId;
            trade.ExchangeID = data.ExchangeId;
            trade.AccountID = data.InvestorId;
            trade.TradeID = data.TradeId;
            trade.Side = GetOrderSide(data.Direction);
            trade.Qty = data.TradeVolume;
            trade.Price = data.TradePrice;
            trade.OpenClose = GetOpenClose(data.OffsetFlag);
            trade.HedgeFlag = GetHedgeFlag(data.HedgeFlag);
            trade.Commission = 0;//TODO收续费以后要计算出来
            trade.Time = GetTime(data.TradeTime);
            trade.Date = GetDate(data.TradingDay);
            trade.ReserveChar64 = ClientId;
            return trade;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private static byte GetQdpOrderSide(OrderSide side)
        {
            return side == OrderSide.Buy ? QdpDirectionType.Buy : QdpDirectionType.Sell;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private static byte GetOffsetFlag(OpenCloseType openClose)
        {
            switch (openClose)
            {
                case OpenCloseType.Close:
                    return QdpOffsetFlagType.Close;
                case OpenCloseType.CloseToday:
                    return QdpOffsetFlagType.CloseToday;
                default:
                    return QdpOffsetFlagType.Open;
            }
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private static byte GetHedgeFlag(HedgeFlagType flag)
        {
            switch (flag)
            {
                case HedgeFlagType.Arbitrage:
                    return QdpHedgeFlagType.Arbitrage;
                case HedgeFlagType.Hedge:
                    return QdpHedgeFlagType.Hedge;
                default:
                    return QdpHedgeFlagType.Speculation;
            }
        }

        public static QdpInputOrder GetInputOrder(OrderField order)
        {
            var data = new QdpInputOrder();
            data.UserOrderLocalId = int.Parse(order.LocalID);
            data.UserCustom = order.ID;
            data.InstrumentId = order.InstrumentID;
            data.ExchangeId = order.ExchangeID.ToUpper();
            data.Direction = GetQdpOrderSide(order.Side);
            data.OffsetFlag = GetOffsetFlag(order.OpenClose);
            data.HedgeFlag = GetHedgeFlag(order.HedgeFlag);
            data.Volume = (int)order.Qty;
            data.LimitPrice = order.Price;
            data.StopPrice = order.StopPx;
            switch (order.Type)
            {
                case OrderType.Market:
                case OrderType.Stop:
                case OrderType.MarketOnClose:
                case OrderType.TrailingStop:
                    data.OrderPriceType = QdpOrderPriceTypeType.AnyPrice;
                    data.TimeCondition = QdpTimeConditionType.Ioc;
                    data.LimitPrice = 0;
                    break;
                default:
                    data.OrderPriceType = QdpOrderPriceTypeType.LimitPrice;
                    data.TimeCondition = QdpTimeConditionType.Gfd;
                    break;
            }
            switch (order.TimeInForce)
            {
                case TimeInForce.IOC:
                    data.TimeCondition = QdpTimeConditionType.Ioc;
                    data.VolumeCondition = QdpVolumeConditionType.AV;
                    break;
                case TimeInForce.FOK:
                    data.TimeCondition = QdpTimeConditionType.Ioc;
                    data.VolumeCondition = QdpVolumeConditionType.CV;
                    break;
                default:
                    data.VolumeCondition = QdpVolumeConditionType.AV;
                    break;
            }
            data.MinVolume = 1;
            data.ForceCloseReason = QdpForceCloseReasonType.NotForceClose;
            data.IsAutoSuspend = 0;
            return data;
        }
    }
}